﻿using System;
using System.Net;

namespace tracert
{
  internal class ICMPHeader
  {
    public static int IcmpHeaderLength = 8;
    private ushort myICMPChecksum; 
    private ushort myICMPId;
    private ushort myICMPSequence; 

    public ICMPHeader()
    {
      myICMPChecksum = 0;
      myICMPId = 0;
      myICMPSequence = 0;
    }

    public byte Type { get; set; }

    public byte Code { get; set; }

    public ushort Checksum
    {
      get { return (ushort)IPAddress.NetworkToHostOrder((short)myICMPChecksum); }

      set { myICMPChecksum = (ushort)IPAddress.HostToNetworkOrder((short)value); }
    }

    public ushort Id
    {
      get { return (ushort)IPAddress.NetworkToHostOrder((short)myICMPId); }

      set { myICMPId = (ushort)IPAddress.HostToNetworkOrder((short)value); }
    }

    public ushort Sequence
    {
      get { return (ushort)IPAddress.NetworkToHostOrder((short)myICMPSequence); }

      set { myICMPSequence = (ushort)IPAddress.HostToNetworkOrder((short)value); }
    }

    public byte[] GetProtocolPacketBytes(byte[] payLoad)
    {
      int offset = 0;

      var icmpPacket = new byte[IcmpHeaderLength + payLoad.Length];
      icmpPacket[offset++] = Type;
      icmpPacket[offset++] = Code;
      icmpPacket[offset++] = 0;
      icmpPacket[offset++] = 0;

      byte[] byteValue = BitConverter.GetBytes(Id);
      Array.Copy(byteValue, 0, icmpPacket, offset, byteValue.Length);
      offset += byteValue.Length;

      byteValue = BitConverter.GetBytes(Sequence);
      Array.Copy(byteValue, 0, icmpPacket, offset, byteValue.Length);
      offset += byteValue.Length;

      if (payLoad.Length > 0)
      {
        Array.Copy(payLoad, 0, icmpPacket, offset, payLoad.Length);
      }

      Checksum = ComputeChecksum(icmpPacket);

      byteValue = BitConverter.GetBytes(Checksum);

      Array.Copy(byteValue, 0, icmpPacket, 2, byteValue.Length);

      return icmpPacket;
    }

    public static UInt16 ComputeChecksum(byte[] buffer)
    {
      UInt32 chcksm = 0;
      byte[] data = buffer;
      int packetsize = buffer.Length;
      int index = 0;
      while (index < packetsize)
      {
        chcksm += Convert.ToUInt32(BitConverter.ToUInt16(data, index));
        index += 2;
      }
      chcksm = (chcksm >> 16) + (chcksm & 0xffff);
      chcksm += (chcksm >> 16);
      return (UInt16)(~chcksm);
    }
  }
}